#include <stdlib.h>

#include "node.h"
#include "board.h"

node * unused_nodes = NULL;
node ** unused_array = NULL;

node * create_node()
{
    node * ret = malloc(sizeof(node));
    ret -> g = NULL;
    ret -> num_children = 0;
    ret -> children = NULL;
    return ret;
}

inline node ** create_array()
{
    return (node**)malloc(4 * sizeof(node*));
}

// Add nodes to a stack to reuse so we don't have to allocate more memory
void push_node(node * n)
{
    if (n == NULL) return;
    int i;
    for (i = 0; i < n -> num_children; ++i)
    {
        push_node(n -> children[i]);
    }
    if (n -> num_children <= 4 &&
        n -> num_children != 2)
            push_array(n -> children);
    else
        free(n -> children);
    n -> num_children = 0;
    n -> children = NULL;
	if (n -> g)
	{
		push_grid(n -> g);
		n -> g = NULL;
	}
    n -> parent = unused_nodes;
    unused_nodes = n;
}

// If we have allocated but unused nodes, reuse them first
node * pop_node()
{
    if (unused_nodes == NULL) return create_node();
    node * ret = unused_nodes;
    unused_nodes = unused_nodes -> parent;
    return ret;
}

// Add nodes to a stack to reuse so we don't have to allocate more memory
void push_array(node ** a)
{
    if (a == NULL) return;
    a[0] = (node *)unused_array;
    unused_array = a;
}

// If we have allocated but unused nodes, reuse them first
node ** pop_array()
{
    if (unused_array == NULL) return create_array();
    node ** ret = unused_array;
    unused_array = (node**)unused_array[0];
    return ret;
}


void set_new_root(node ** old_root, node * new_root)
{
    node * temp = *old_root, * parent = new_root -> parent;
    *old_root = new_root;
    if (parent != NULL) {
        int i = 0;
        for (i = 0; i < parent -> num_children; ++i) {
            if (parent -> children[i] == new_root) break;
        }
        parent -> children[i] = parent -> children[--parent -> num_children];
    }
    push_node(temp);
}

// free malloc'ed memory
void destroy_tree(node * tree)
{
    if (tree == NULL) return;
    int i;
    for (i = 0; i < tree -> num_children; ++i)
    {
        destroy_tree(tree -> children[i]);
    }
    free(tree -> children);
    push_grid(tree -> g);
    free(tree);
}

void cleanup_unused_nodes()
{
    while (unused_nodes != NULL)
    {
        node * cur = unused_nodes -> parent;
        free(unused_nodes);
        unused_nodes = cur;
    }
}
